package com.sinog.front.config.log;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.sinog.core.model.UserDTO;
import com.sinog.front.feign.biz.LogService;
import com.sinog.front.service.user.UserFacade;
import com.sinog.front.util.UserToLogUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * @desc 系统管理 - 系统日志 - 系统日志 - 日志切面
 * @author lzq
 * @date -
 */
@Aspect
@Component
public class LogAspect {

    @Autowired
    private UserFacade userFacade;
    @Resource
    private LogService logService;
    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
    private static final String DATEFORMAT = "yyyy-MM-dd HH:mm:ss";
    private static final String STRING_START = "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n";
    private static final String STRING_END = "\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n";

    /**
     * 定义切点
     */
    @Pointcut("execution(* com.sinog.front.controller..*(..))")
    public void serviceLog() {
    }

    /**
     * 环绕通知
     * @param joinPoint joinPoint
     * @return Object
     */
    @Around("serviceLog()")
    public Object around(ProceedingJoinPoint joinPoint) {
        //1、定义返回结果
        Object result;
        try {
            //2、获取当前登录用户
            UserDTO userDTO = userFacade.getAuthentication();
            //3、获取客户端ip
            String ip = "";
            HttpServletRequest request;
            int length = joinPoint.getArgs().length;
            for(int i = 0; i < length; i++) {
                Object ll = joinPoint.getArgs()[i];
                if(ll instanceof HttpServletRequest) {
                    request = (HttpServletRequest)ll;
                    ip = request.getHeader("X-Forwarded-For");
                    // 取不到forwarded地址就取实际的ip地址
                    if(null == ip || ip.isEmpty()) {
                        ip = request.getRemoteAddr();
                    } else {
                        // 如果有多级反向代理，返回的是一组ip，取第一个
                        ip = ip.split(",")[0];
                    }
                    break;
                }
            }
            //4、反射
            MethodSignature signature = (MethodSignature)joinPoint.getSignature();
            Method method = signature.getMethod();
            Class<?> targetClass = method.getDeclaringClass();
            //5、定义类方法字符串
            StringBuffer classAndMethod = new StringBuffer(16);
            //6、获取目标类方法是否添加日志注解
            Log classAnnotation = targetClass.getAnnotation(Log.class);
            Log methodAnnotation = method.getAnnotation(Log.class);
            //7、日志注解定义在类/方法上，设置ignore为false直接返回，设置为true继续往下走
            if(null != classAnnotation) {
                if(classAnnotation.ignore()) {
                    return joinPoint.proceed();
                }
                classAndMethod.append(classAnnotation.value()).append("-");
            }
            if(null != methodAnnotation) {
                if(methodAnnotation.ignore()) {
                    return joinPoint.proceed();
                }
                classAndMethod.append(methodAnnotation.value());
            }
            //8、获取方法接口
            String target = targetClass.getName() + "#" + method.getName();
            StringBuilder params = new StringBuilder();
            //9、解析参数打日志
            if(null != methodAnnotation && !methodAnnotation.ignore()) {
                try {
                    int length1 = joinPoint.getArgs().length;
                    for(int i = 0; i < length1; i++) {
                        Object ll = joinPoint.getArgs()[i];
                        if(!(ll instanceof HttpServletRequest || ll instanceof MultipartFile)) {
                            params.append(JSON.toJSONStringWithDateFormat(joinPoint.getArgs()[i],DATEFORMAT,SerializerFeature.WriteMapNullValue));
                        }
                    }
                } catch(com.alibaba.fastjson.JSONException jsonException) {
                    params = new StringBuilder("参数解析错误");
                    log.error("参数解析错误");
                }
                log.info(STRING_START + "【{}】: 开始调用--> {}\n【参数】:{}",classAndMethod,target,params);
                long start = System.currentTimeMillis();
                result = joinPoint.proceed();
                long timeConsuming = System.currentTimeMillis() - start;
                log.info("\n--------------------------------------------------------------------------------------------------\n" + "【{}】: 调用结束<-- {}\n【返回值】:{}\n【耗时】:{}ms" + STRING_END,classAndMethod,target,JSON.toJSONStringWithDateFormat(result,DATEFORMAT,SerializerFeature.WriteMapNullValue),timeConsuming);
                //10、记录日志信息到数据库
                try {
                    com.sinog.core.domain.Log logDomain = UserToLogUtil.converse(userDTO,methodAnnotation.type(),methodAnnotation.value(),params.toString(),target,ip);
                    logService.saveLog(logDomain);
                } catch(RuntimeException e) {
                    log.error("日志信息持久化数据库错误");
                }
            } else {
                result = joinPoint.proceed();
            }
            return result;
        } catch(Throwable throwable) {
            log.error(throwable.getMessage(),throwable);
        }
        return null;
    }
}